
package org.sopt.dev.ImgKnight.agent.Executor;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.Callable;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.log4j.Logger;

import org.sopt.dev.ImgKnight.agent.Agent;
import org.sopt.dev.ImgKnight.agent.URL.Util.UrlUtil;
import org.sopt.dev.ImgKnight.agent.database.HarvestVO;

public class TakeResource implements Callable<List<HarvestVO>> {

	private List<String> extracted = null;
	
	private String countryCode = null;
	
	private String countryName = null;
	
	private Agent agent = null;
	
	private static SimpleDateFormat prefix = new SimpleDateFormat("yyy-MM-dd");
	
	private static SimpleDateFormat middlefix = new SimpleDateFormat("HH");
	
	private static SimpleDateFormat postfix = new SimpleDateFormat("mm-ss-");
	
	private Logger exceptionLogger = null;
	
	public TakeResource(List<String> extracted, String countryCode, String countryName) {
		this.extracted = extracted;
		this.countryCode = countryCode;
		this.countryName = countryName;
		this.agent = Agent.getInstance();
		exceptionLogger = Logger.getLogger("agent.exception");
	}
	
	@Override
	public List<HarvestVO> call() {
		//long start = System.currentTimeMillis();
		List<HarvestVO> downloaded = new Vector<HarvestVO>(extracted.size());
		GetMethod getMethod = null;
		int resourceMinimumByte = agent.getAgentConfig().ResourceMinimumByte;
		int discardCount = 0;
		URL url = null;
		String baseUrl = extracted.remove(0);
		
		int bufferSize = agent.getAgentConfig().FileDownloadBufferSize;
		byte [] buffer = new byte[bufferSize];
		
		for(String r : extracted) {
			try {
				int resourceByteLength = 0;
				url = new URL(r);
				if( resourceMinimumByte > 0 ) {
					resourceByteLength = getResourceByteLength(url);
					if( resourceByteLength == -1 )
						continue;
				}
				if( resourceByteLength < resourceMinimumByte ) {
					++discardCount;
					continue;
				}
				getMethod = agent.getHttpHandler().getHttpGetMethod(url);
				if( getMethod == null )
					continue;
				
				Date now = new Date();
				int normalizedRcPath = 0;
				String rcPath = url.toString();
				byte[] baseUrlByte = new byte[baseUrl.getBytes().length];
				byte[] rcPathByte = new byte[rcPath.getBytes().length];
				baseUrlByte = baseUrl.getBytes();
				rcPathByte = rcPath.getBytes();
				for(int i=0, j=0; i<rcPathByte.length; i++) {
					rcPathByte[i] = (byte)(rcPathByte[i] ^ baseUrlByte[j]);
					normalizedRcPath += (int)rcPathByte[i];
					j = (++j < baseUrlByte.length ? j : 0);
				}
					
				String filePath = agent.getAgentConfig().DownloadBaseDir+
				prefix.format(now)+"/TID_"+Thread.currentThread().getId()+"/"+middlefix.format(now)+
				"/"+postfix.format(now) +normalizedRcPath;
				File file = new File(filePath);
				if( file == null ) {
					exceptionLogger.warn("Create file exception");
					continue;
				}
				file.getParentFile().mkdirs();
				if( makeResourceFile(buffer, bufferSize, file, getMethod.getResponseBodyAsStream()) ) {
					String hostUrl = UrlUtil.getHostURL(baseUrl);
					if( hostUrl != null ) {
						HarvestVO harvestTransfer = new HarvestVO();
						harvestTransfer.setSite(hostUrl);
						harvestTransfer.setPageUrl(baseUrl);
						harvestTransfer.setResourceUrl(r);
						harvestTransfer.setFileName(file.toString());
						harvestTransfer.setFileSize(resourceByteLength);
						harvestTransfer.setCountryCode(countryCode);
						harvestTransfer.setCountryName(countryName);
						harvestTransfer.setTimestamp(new Timestamp(System.currentTimeMillis()));
						downloaded.add(harvestTransfer);
					}
				}
			} catch (MalformedURLException e) {
				exceptionLogger.warn("MalformedURLException: "+e+": "+r);
				continue;
			} catch (Exception e) {
				exceptionLogger.warn("Unexpected exception: "+e+": "+r);
				continue;
			}finally {
				if( getMethod != null ) getMethod.releaseConnection();
			}
		}
		int missCount = extracted.size()-downloaded.size()-discardCount;
		if( missCount > 0 )
			agent.onMissedDownload(missCount);
		if( discardCount > 0 )
			agent.onDiscardDownload(discardCount);
		//long end = System.currentTimeMillis();
		//serviceLogger.debug("Resource: "+downloaded.size()+" downloaded");
		//serviceLogger.debug("Required time: " + (end-start) + " milli-second");
		return downloaded;
	}
	
	private int getResourceByteLength(URL url) {
		int length = -1;
		HeadMethod headMethod = null;
		try {
			headMethod = agent.getHttpHandler().getHttpHeadMethod(url);
			if( headMethod == null ) {
				return length;
			}
			Header header = headMethod.getResponseHeader("Content-Length");
			if( header != null ) {
				String lentmp = header.getValue();
				if( lentmp != null ) {
					length = Integer.parseInt(lentmp);
				}
			}
		} catch (Exception e) {
			exceptionLogger.warn("Unexpected exception: "+e);
		} finally {
			if( headMethod != null ) headMethod.releaseConnection();
		}
		return length;
	}
	
	private boolean makeResourceFile(byte[] buffer, int bufferSize, File file, InputStream inputStream) {
		BufferedOutputStream out = null;
		BufferedInputStream in = null;
		try {
			out = new BufferedOutputStream(new FileOutputStream(file));
			in = new BufferedInputStream(inputStream);
			int readByte = 0;
			while( (readByte=in.read(buffer, 0, bufferSize)) != -1 )
				out.write(buffer, 0, readByte);
			return true;
		} catch (IOException e) {
			exceptionLogger.warn("IOException: "+e);
			return false;
		} catch (Exception e) {
			exceptionLogger.warn("Unexpected exception: "+e);
			e.getCause().printStackTrace();
			return false;
		} finally {
			if( in != null ) try{ in.close(); } catch (Exception e){}
			if( out != null ) try{ out.close(); } catch (Exception e){}
		}
	}
	
	public String getCountryCode() {
		return countryCode;
	}

	public String getCountryName() {
		return countryName;
	}
	
}
